﻿/*
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at 
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  
 */
using System;
using java = biz.ritter.javapi;

namespace biz.ritter.javapi.util
{

    /**
     * Observable is used to notify a group of Observer objects when a change
     * occurs. On creation, the set of observers is empty. After a change occurred,
     * the application can call the {@link #notifyObservers()} method. This will
     * cause the invocation of the {@code update()} method of all registered
     * Observers. The order of invocation is not specified. This implementation will
     * call the Observers in the order they registered. Subclasses are completely
     * free in what order they call the update methods.
     *
     * @see Observer
     */
    public class Observable {

        protected internal List<Observer> observers = new ArrayList<Observer>();

        protected internal bool changed = false;

        /**
         * Constructs a new {@code Observable} object.
         */
        public Observable() : base () {
        }

        /**
         * Adds the specified observer to the list of observers. If it is already
         * registered, it is not added a second time.
         * 
         * @param observer
         *            the Observer to add.
         */
        public virtual void addObserver(Observer observer)
        {
            if (observer == null) {
                throw new java.lang.NullPointerException();
            }
            lock (this) {
                if (!observers.contains(observer))
                    observers.add(observer);
            }
        }

        /**
         * Clears the changed flag for this {@code Observable}. After calling
         * {@code clearChanged()}, {@code hasChanged()} will return {@code false}.
         */
        protected virtual void clearChanged()
        {
            changed = false;
        }

        /**
         * Returns the number of observers registered to this {@code Observable}.
         * 
         * @return the number of observers.
         */
        public virtual int countObservers()
        {
            return observers.size();
        }

        /**
         * Removes the specified observer from the list of observers. Passing null
         * won't do anything.
         * 
         * @param observer
         *            the observer to remove.
         */
        public virtual void deleteObserver(Observer observer)
        {
            lock (this) {
                observers.remove(observer);
            }
        }

        /**
         * Removes all observers from the list of observers.
         */
        public virtual void deleteObservers()
        {
            lock (this) {
                observers.clear();
            }
        }

        /**
         * Returns the changed flag for this {@code Observable}.
         * 
         * @return {@code true} when the changed flag for this {@code Observable} is
         *         set, {@code false} otherwise.
         */
        public virtual bool hasChanged()
        {
            return changed;
        }

        /**
         * If {@code hasChanged()} returns {@code true}, calls the {@code update()}
         * method for every observer in the list of observers using null as the
         * argument. Afterwards, calls {@code clearChanged()}.
         * <p>
         * Equivalent to calling {@code notifyObservers(null)}.
         */
        public virtual void notifyObservers()
        {
            notifyObservers(null);
        }

        /**
         * If {@code hasChanged()} returns {@code true}, calls the {@code update()}
         * method for every Observer in the list of observers using the specified
         * argument. Afterwards calls {@code clearChanged()}.
         * 
         * @param data
         *            the argument passed to {@code update()}.
         */
        public virtual void notifyObservers(Object data)
        {
            int size = 0;
            Observer[] arrays = null;
            lock (this) {
                if (hasChanged()) {
                    clearChanged();
                    size = observers.size();
                    arrays = new Observer[size];
                    observers.toArray(arrays);
                }
            }
            if (arrays != null) {
                foreach (Observer observer in arrays) {
                    observer.update(this, data);
                }
            }
        }

        /**
         * Sets the changed flag for this {@code Observable}. After calling
         * {@code setChanged()}, {@code hasChanged()} will return {@code true}.
         */
        protected virtual void setChanged() {
            changed = true;
        }
    }
}
